;;;;

;;;;
;;; Nehe Lesson #11 - A Waving Flag

(in-package :xlui)

(defun draw-cube (twidth theight)
  "Assumes the GLContext has been set!"
  (let ((v0 {1.0 1.0 1.0})
        (v1 {-1.0 1.0 1.0})
        (v2 {-1.0 -1.0 1.0})
        (v3 {1.0 -1.0 1.0})
        (v4 {1.0 -1.0 -1.0})
        (v5 {1.0 1.0 -1.0})
        (v6 {-1.0 1.0 -1.0})
        (v7 {-1.0 -1.0 -1.0}))
    (gl:with-gl GL_QUADS

      ;; Front Face
      (glNormal3f 0.0 0.0 1.0)
      (glTexCoord2f 0.0 0.0)         (glVertex3fv v2)
      (glTexCoord2f twidth 0.0)      (glVertex3fv v3)
      (glTexCoord2f twidth theight)  (glVertex3fv v0)
      (glTexCoord2f 0.0 theight)     (glVertex3fv v1)
      ;; Back Face
      (glNormal3f 0.0 0.0 -1.0)
      (glTexCoord2f twidth 0.0)      (glVertex3fv v7)
      (glTexCoord2f twidth theight)  (glVertex3fv v6)
      (glTexCoord2f 0.0 theight)     (glVertex3fv v5)
      (glTexCoord2f 0.0 0.0)         (glVertex3fv v4)
      ;; Top Face
      (glNormal3f 0.0 1.0 0.0)
      (glTexCoord2f 0.0 theight)     (glVertex3fv v6)
      (glTexCoord2f 0.0 0.0)         (glVertex3fv v1)
      (glTexCoord2f twidth 0.0)      (glVertex3fv v0)
      (glTexCoord2f twidth theight)  (glVertex3fv v5)
      ;; Bottom Face
      (glNormal3f 0.0 -1.0 0.0)
      (glTexCoord2f twidth theight)  (glVertex3fv v4)
      (glTexCoord2f 0.0 theight)     (glVertex3fv v7)
      (glTexCoord2f 0.0 0.0)         (glVertex3fv v2)
      (glTexCoord2f twidth 0.0)      (glVertex3fv v3)
      ;; Right Face
      (glNormal3f 1.0 0.0 0.0)
      (glTexCoord2f twidth 0.0)      (glVertex3fv v4)
      (glTexCoord2f twidth theight)  (glVertex3fv v5)
      (glTexCoord2f 0.0 theight)     (glVertex3fv v0)
      (glTexCoord2f 0.0 0.0)         (glVertex3fv v3)
      ;; Left Face
      (glNormal3f -1.0 0.0 0.0)
      (glTexCoord2f 0.0 0.0)         (glVertex3fv v2)
      (glTexCoord2f twidth 0.0)      (glVertex3fv v1)
      (glTexCoord2f twidth theight)  (glVertex3fv v6)
      (glTexCoord2f 0.0 theight)     (glVertex3fv v7))))

;;;; **** ;;;;

(defclass nehe-view (agent-3d-view)
  ())

(defmethod texture-file ((view nehe-view) texture-name)
  (native-path "dt:data;nehe" texture-name))

(defmethod init ((view nehe-view))
  (glShadeModel GL_SMOOTH)
  (glClearColor 0.0 0.0 0.0 0.5)
  (glClearDepth 1.0d0)
  (glEnable GL_DEPTH_TEST)
  (glDepthFunc GL_LEQUAL)
  (glEnable GL_LIGHT0)
  (glEnable GL_LIGHTING)
  (glEnable GL_COLOR_MATERIAL)
  (glHint GL_PERSPECTIVE_CORRECTION_HINT GL_NICEST))

(defclass nehe-lesson-11 (nehe-view)
  ())

(defmethod init  ((view nehe-lesson-11))
;; Back facing polygons are filled, while front facing are only outlined.
;; Apparently this is a setting of "personal preference" and the topic is more
;; fully discussedin the Red Book.
  (glShadeModel GL_SMOOTH)
  (glEnable GL_TEXTURE_2D)
  (glClearColor 0.0 0.0 0.0 0.5)
  (glClearDepth 1.0d0)
  (glEnable GL_DEPTH_TEST)
  (glDepthFunc GL_LEQUAL)
  (glHint GL_PERSPECTIVE_CORRECTION_HINT GL_NICEST)
  (glPolygonMode GL_BACK GL_FILL)
  (glPolygonMode GL_FRONT GL_LINE))
  
(defclass flag (agent-3d)
  ((wiggle-count :accessor wiggle-count :initarg :wiggle-count :initform 0
                 :documentation "How fast the flag waves.")
   (points :accessor points :documentation "Points on the mesh of the texture that will be 'waved.'")))

(defmethod initialize-instance :after ((self flag) &rest initargs)
  (declare (ignore initargs))
  (let ((arr (make-array '(45 45 3) :initial-element 0.0)))
    (dotimes (x 45)
      (dotimes (y 45)
        (setf (aref arr x y 0) (- (/ x 5.0) 4.5)
              (aref arr x y 1) (- (/ y 5.0) 4.5)
              (aref arr x y 2) (sin (* 2 (coerce PI 'single-float) (/ (* (/ x 5.0) 40.0) 360.0))))))
    (setf (points self) arr)))

(defmethod draw ((agent flag))
  (with-slots (points) agent
    (use-texture (view agent) "Tim.bmp")
    (gl:with-gl GL_QUADS
      (dotimes (x 44)
        (dotimes (y 44)
          (let ((xb (1+ x))           (yb (1+ y))
                (fx (/ x 44.0))       (fy (/ y 44.0))
                (fxb (/ (1+ x) 44.0)) (fyb (/ (1+ y) 44.0))) ;; Texture coordinates for the flag.
            (glTexCoord2f fx fy)
            (glVertex3f (aref points x y 0) (aref points x y 1) (aref points x y 2))
            (glTexCoord2f fx fyb)
            (glVertex3f (aref points x yb 0) (aref points x yb 1) (aref points x yb 2))
            (glTexCoord2f fxb fyb)
            (glVertex3f (aref points xb yb 0) (aref points xb yb 1) (aref points xb yb 2))
            (glTexCoord2f fxb fy)
            (glVertex3f (aref points xb y 0) (aref points xb y 1) (aref points xb y 2))))))
    (if (= (wiggle-count agent) 10)
      (progn
        (dotimes (y 45)
          (let ((hold (aref points 0 y 2)))
            (dotimes (x 44)
              (setf (aref points x y 2) (aref points (1+ x) y 2)))
            (setf (aref points 44 y 2) hold)))
        (setf (wiggle-count agent) 0)))
    (incf (wiggle-count agent))
    (incf (pitch agent) 0.03) ;; Rotation around x plane.
    (incf (heading agent) 0.02) ;; Rotation around y plane.
    (incf (roll agent) 0.04) ;; Rotation around z plane.
    (glDisable GL_TEXTURE_2D)))

(defun nehe-lesson-11 ()
  (slui (:application-window :title "Nehe Lesson #10"
         (:nehe-lesson-11 :name "FlagView"
          (:flag :name "Flag")))))

;;;;
;;; Nehe Lesson #12 - Display Lists

(defparameter *initial-box-colors* (list {1.0 0.0 0.0}
                                         {1.0 0.5 0.0}
                                         {1.0 1.0 0.0}
                                         {0.0 1.0 0.0}
                                         {0.0 1.0 1.0}))
(defparameter *initial-top-colors* (list {0.5 0.0 0.0}
                                         {0.5 0.25 0.0}
                                         {0.5 0.5 0.0}
                                         {0.0 0.5 0.0}
                                         {0.0 0.5 0.5}))
(defclass q-bert-blocks (agent-3d)
  ((box-colors :accessor box-colors
               :initform (make-array '(5) :initial-contents *initial-box-colors*))
   (top-colors :accessor top-colors
               :initform (make-array '(5) :initial-contents *initial-top-colors*))
   (box :accessor box :initform nil :documentation "Reference for the display lists.")
   (top :accessor top :initform nil)))

(defun build-lists (q-bert)
  (with-slots (box top) q-bert
    (setf box (glGenLists 2))
    (glNewList box GL_COMPILE)
    (draw-cube 1.0 1.0)
    (glEndList)

    (setf top (1+ box))
    (glNewList top GL_COMPILE)
    (gl:with-gl GL_QUADS
      (glTexCoord2f 0.0 1.0) (glVertex3f -1.0 1.0 -1.0)
      (glTexCoord2f 0.0 0.0) (glVertex3f -1.0 1.0 1.0)
      (glTexCoord2f 1.0 0.0) (glVertex3f 1.0 1.0 1.0)
      (glTexCoord2f 1.0 1.0) (glVertex3f 1.0 1.0 -1.0))
    (glEndList)))

(defmethod draw ((agent q-bert-blocks))
  (unless (and (slot-boundp agent 'box)
               (not (null (box agent))))
    (build-lists agent))
  (dotimes (yloop 5)
    (dotimes (xloop (1+ yloop))
      (glPushMatrix)
      (glTranslatef (+ 1.4 (- (* xloop 2.8) (* yloop 1.4)))
                    (- 6.0 (* yloop 2.4) 7.0)
                    (- 20.0))
      (glRotatef (- 45.0 (+ (* 2.0 yloop) (pitch agent))) 1.0 0.0 0.0)
      (glRotatef (+ 45.0 (heading agent)) 0.0 1.0 0.0)
      (glColor3fv (aref (box-colors agent) yloop))
      (glCallList (box agent))
      (glColor3fv (aref (top-colors agent) yloop))
      (glCallList (top agent))
      (glPopMatrix))))
      
(defun nehe-lesson-12 ()
  (slui (:application-window :title "NeHe Lesson #12"
                             (:nehe-view :name "QBert"
                                         (:q-bert-blocks)))));;;;
;;; NeHe Lesson #16 - Fog

(defclass nehe-fog-view (nehe-view)
  ((filter :accessor filter :initarg :filter)
   (fog-modes :accessor fog-mode :initarg :fog-mode :initform (list GL_EXP GL_EXP2 GL_LINEAR))
   (fog-filter :accessor fog-filter :initarg :fog-filter :initform 0)
   (fog-color :accessor fog-color :initarg :fog-color :initform {0.5 0.5 0.5 1.0})))

(defmethod init ((view nehe-fog-view))
  (with-slots (fog-modes fog-filter fog-color) view
    (glShadeModel GL_SMOOTH)
    (glClearColor 0.5 0.5 0.5 1.0)
    (glClearDepth 1.0d0)
    (glEnable GL_DEPTH_TEST)
    (glDepthFunc GL_LEQUAL)
    (glEnable GL_LIGHT0)
    (glEnable GL_LIGHTING)
    (glEnable GL_COLOR_MATERIAL)
    (glHint GL_PERSPECTIVE_CORRECTION_HINT GL_NICEST)
    (glFogi GL_FOG_MODE (nth fog-filter fog-modes))
    (glFogfv GL_FOG_COLOR fog-color)
    (glFogf GL_FOG_DENSITY 0.35)
    (glHint GL_FOG_HINT GL_DONT_CARE)
    (glFogf GL_FOG_START 1.0)
    (glFogf GL_FOG_END 5.0)
    (glEnable GL_FOG)))

(defmethod draw ((view nehe-fog-view))
  (glEnable GL_TEXTURE_2D)
  (use-texture view "BoxImg01.jpg")
  (glScalef 3.0 3.0 3.0)
  (draw-cube 1.0 1.0)
  (glDisable GL_TEXTURE_2D))
    
(defun nehe-lesson-16 ()
  (slui (:application-window :title "NeHe Lesson #16 - Fog"
                             (:nehe-fog-view :name "FogView"))))
        